home *** CD-ROM | disk | FTP | other *** search
- /*- MAKENODE.C --------------------------------------------------------------*
- Recursively create nodes and return the pointers.
- *---------------------------------------------------------------------------*/
-
- struct Node *CreateNode(struct Seg *ts)
- {
- struct Node *tn;
- struct Seg *rights = NULL;
- struct Seg *lefts = NULL;
-
- tn = GetMemory( sizeof( struct Node)); /* Create a node*/
-
- DivideSegs(ts,&rights,&lefts); /* Divide node in two*/
-
- num_nodes++;
-
- tn->x = node_x; /* store node line info*/
- tn->y = node_y;
- tn->dx = node_dx;
- tn->dy = node_dy;
-
- FindLimits(lefts); /* Find limits of vertices */
-
- tn->maxy2 = lmaxy;
- tn->miny2 = lminy;
- tn->minx2 = lminx;
- tn->maxx2 = lmaxx;
-
- if(IsItConvex(lefts)) /* Check lefthand side*/
- {
- tn->nextl = CreateNode(lefts); /* still segs remaining*/
- tn->chleft = 0;
- }
- else
- {
- tn->nextl = NULL;
- tn->chleft = CreateSSector(lefts) | 0x8000;
- }
-
- FindLimits(rights); /* Find limits of vertices*/
-
- tn->maxy1 = lmaxy;
- tn->miny1 = lminy;
- tn->minx1 = lminx;
- tn->maxx1 = lmaxx;
-
- if(IsItConvex(rights)) /* Check righthand side*/
- {
- tn->nextr = CreateNode(rights); /* still segs remaining*/
- tn->chright = 0;
- }
- else
- {
- tn->nextr = NULL;
- tn->chright = CreateSSector(rights) | 0x8000;
- }
-
- return tn;
- }
-
- /*---------------------------------------------------------------------------*
- Split a list of segs (ts) into two using the method described at bottom of
- file, this was taken from OBJECTS.C in the DEU5beta source.
-
- This is done by scanning all of the segs and finding the one that does
- the least splitting and has the least difference in numbers of segs on either
- side.
- If the ones on the left side make a SSector, then create another SSector
- else put the segs into lefts list.
- If the ones on the right side make a SSector, then create another SSector
- else put the segs into rights list.
- *---------------------------------------------------------------------------*/
-
- void DivideSegs(struct Seg *ts,struct Seg **rs,struct Seg **ls)
- {
- struct Seg *rights,*lefts;
- struct Seg *tmps,*best,*news,*prev;
- struct Seg *add_to_rs,*add_to_ls;
- struct Seg *new_best,*new_rs,*new_ls;
-
- struct Seg *strights,*stlefts;
-
- int num_secs_r,num_secs_l,last_sec_r,last_sec_l;
-
- int num,least_splits,least;
- int fv,tv,num_new = 0;
- int bangle,cangle,cangle2,cfv,ctv,dx,dy;
- short int x,y,val;
-
- FindLimits(ts); /* Find limits of this set of Segs*/
-
- sp.halfsx = (lmaxx - lminx) / 2; /* Find half width of Node*/
- sp.halfsy = (lmaxy - lminy) / 2;
- sp.halfx = lminx + sp.halfsx; /* Find middle of Node*/
- sp.halfy = lminy + sp.halfsy;
-
- best = PickNode(ts); /* Pick best node to use.*/
-
- if(best == NULL) ProgError("Couldn't pick nodeline!");
-
- node_x = vertices[best->start].x;
- node_y = vertices[best->start].y;
- node_dx = vertices[best->end].x-vertices[best->start].x;
- node_dy = vertices[best->end].y-vertices[best->start].y;
-
- /* When we get to here, best is a pointer to the partition seg.
- Using this partition line, we must split any lines that are intersected
- into a left and right half, flagging them to be put their respective sides
- Ok, now we have the best line to use as a partitioning line, we must
- split all of the segs into two lists (rightside & leftside). */
-
- rights = NULL; /* Start off with empty*/
- lefts = NULL; /* lists.*/
- strights = NULL; /* Start off with empty*/
- stlefts = NULL; /* lists.*/
-
- psx = vertices[best->start].x; /* Partition line coords*/
- psy = vertices[best->start].y;
- pex = vertices[best->end].x;
- pey = vertices[best->end].y;
- pdx = psx - pex; /* Partition line DX,DY*/
- pdy = psy - pey;
-
- for(tmps=ts;tmps;tmps=tmps->next)
- {
- progress(); /* Something for the user to look at.*/
- add_to_rs = NULL;
- add_to_ls = NULL;
- if(tmps != best)
- {
- lsx = vertices[tmps->start].x; /* Calculate this here, cos it doesn't*/
- lsy = vertices[tmps->start].y; /* change for all the interations of*/
- lex = vertices[tmps->end].x; /* the inner loop!*/
- ley = vertices[tmps->end].y;
- val = DoLinesIntersect();
- if((val&2 && val&64) || (val&4 && val&32)) /* If intersecting !!*/
- {
- ComputeIntersection(&x,&y);
- /* printf("Splitting Linedef %d at %d,%d\n",tmps->linedef,x,y);*/
-
- vertices = ResizeMemory(vertices, sizeof(struct Vertex) * (num_verts+1));
- vertices[num_verts].x = x;
- vertices[num_verts].y = y;
-
- news = GetMemory(sizeof( struct Seg));
-
- news->next = tmps->next;
- tmps->next = news;
- news->start = num_verts;
- news->end = tmps->end;
- tmps->end = num_verts;
- news->linedef = tmps->linedef;
- news->angle = tmps->angle;
- news->flip = tmps->flip;
- news->dist = SplitDist(news);
- /* printf("splitting dist = %d\n",news->dist);*/
- /* printf("splitting vertices = %d,%d,%d,%d\n",tmps->start,tmps->end,news->start,news->end);*/
- if(val&32) add_to_ls = tmps;
- if(val&64) add_to_rs = tmps;
- if(val&2) add_to_ls = news;
- if(val&4) add_to_rs = news;
- tmps = news;
- num_verts++;
- num_new++;
- }
- else
- { /* Not split, which side ?*/
- if(val&34) add_to_ls = tmps;
- if(val&68) add_to_rs = tmps;
- if(val&1 && val&16) /* On same line*/
- {
- if(tmps->flip == best->flip) add_to_rs = tmps;
- if(tmps->flip != best->flip) add_to_ls = tmps;
- }
- }
- }
- else add_to_rs = tmps; /* This is the partition line*/
-
- /* printf("Val = %X\n",val);*/
-
- if(add_to_rs) /* CHECK IF SHOULD ADD RIGHT ONE */
- {
- new_rs = GetMemory(sizeof(struct Seg));
- if(add_to_rs == best) new_best = new_rs;
- new_rs->start = add_to_rs->start;
- new_rs->end = add_to_rs->end;
- new_rs->linedef = add_to_rs->linedef;
- new_rs->angle = add_to_rs->angle;
- new_rs->flip = add_to_rs->flip;
- new_rs->dist = add_to_rs->dist;
- new_rs->next = NULL;
- if(!rights) strights = rights = new_rs;
- else
- {
- rights->next = new_rs;
- rights = new_rs;
- }
- }
-
- if(add_to_ls) /* CHECK IF SHOULD ADD LEFT ONE */
- {
- new_ls = GetMemory(sizeof(struct Seg));
- if(add_to_ls == best) new_best = new_ls;
- new_ls->start = add_to_ls->start;
- new_ls->end = add_to_ls->end;
- new_ls->linedef = add_to_ls->linedef;
- new_ls->angle = add_to_ls->angle;
- new_ls->flip = add_to_ls->flip;
- new_ls->dist = add_to_ls->dist;
- new_ls->next = NULL;
- if(!lefts) stlefts = lefts = new_ls;
- else
- {
- lefts->next = new_ls;
- lefts = new_ls;
- }
- }
- }
-
- if(strights == NULL)
- {
- /* printf("No right side, moving partition into right side\n");*/
- strights = rights = new_best;
- prev = NULL;
- for(tmps=stlefts;tmps;tmps=tmps->next)
- {
- if(tmps == new_best)
- {
- if(prev != NULL) prev->next=tmps->next;
- else stlefts=tmps->next;
- }
- prev=tmps;
- }
- prev->next = NULL;
- }
-
- if(stlefts == NULL)
- {
- /* printf("No left side, moving partition into left side\n");*/
- stlefts = lefts = new_best;
- prev = NULL;
- for(tmps=strights;tmps;tmps=tmps->next)
- {
- if(tmps == new_best)
- {
- if(prev != NULL) prev->next=tmps->next;
- else strights=tmps->next;
- }
- prev=tmps;
- }
- stlefts->next = NULL;
- prev->next = NULL; /* Make sure end of list = NULL*/
- }
-
- if(rights->next != NULL) rights->next = NULL;
- if(lefts->next != NULL) lefts->next = NULL;
-
- for(tmps=ts;tmps;tmps=best)
- {
- best=tmps->next;
- free(tmps);
- }
-
- /* printf("Made %d new Vertices and Segs\n",num_new);*/
-
- *rs = strights ; *ls = stlefts;
- }
-
- /*--------------------------------------------------------------------------*/
-
- int IsItConvex( struct Seg *ts)
- {
- struct Seg *line,*check;
- int sector,val;
-
- if (ts->flip) sector = sidedefs[linedefs[ ts->linedef].sidedef2].sector;
- else sector = sidedefs[linedefs[ts->linedef].sidedef1].sector;
-
- for (line = ts->next; line; line = line->next)
- {
- if (line->flip)
- {
- if (sidedefs[ linedefs[ line->linedef].sidedef2].sector != sector)
- return TRUE;
- }
- else
- {
- if (sidedefs[ linedefs[ line->linedef].sidedef1].sector != sector)
- return TRUE;
- }
- }
-
- /* all of the segs must be on the same side all the other segs */
-
- for(line=ts;line;line=line->next)
- {
- psx = vertices[line->start].x;
- psy = vertices[line->start].y;
- pex = vertices[line->end].x;
- pey = vertices[line->end].y;
- pdx = (psx - pex); /* Partition line DX,DY*/
- pdy = (psy - pey);
- for(check=ts;check;check=check->next)
- {
- if(line!=check)
- {
- lsx = vertices[check->start].x; /* Calculate this here, cos it doesn't*/
- lsy = vertices[check->start].y; /* change for all the interations of*/
- lex = vertices[check->end].x; /* the inner loop!*/
- ley = vertices[check->end].y;
- val = DoLinesIntersect();
- if(val&34) return TRUE;
- }
- }
- }
-
- /* no need to split the list: these Segs can be put in a SSector */
- return FALSE;
- }
-
- /*--------------------------------------------------------------------------*/
-
- int CreateSSector(struct Seg *tmps)
- {
- int n;
-
- if(num_ssectors == 0)
- {
- ssectors = GetMemory(sizeof(struct SSector));
- }
- else
- {
- ssectors = ResizeMemory(ssectors,sizeof(struct SSector)*(num_ssectors+1));
- }
-
- ssectors[num_ssectors].first = num_psegs;
-
- n = num_psegs;
-
- /* printf("\n");*/
-
- for(;tmps;tmps=tmps->next)
- {
- if(num_psegs == 0)
- {
- psegs = GetMemory(sizeof(struct Pseg));
- }
- else
- {
- psegs = ResizeMemory(psegs,sizeof(struct Pseg)*(num_psegs+1));
- }
-
- psegs[num_psegs].start = tmps->start;
- psegs[num_psegs].end = tmps->end;
- psegs[num_psegs].angle = tmps->angle;
- psegs[num_psegs].linedef = tmps->linedef;
- psegs[num_psegs].flip = tmps->flip;
- psegs[num_psegs].dist = tmps->dist;
- /*
- printf("%d,%d,%u,%d,%d,%u\n",
- psegs[num_psegs].start,
- psegs[num_psegs].end,
- psegs[num_psegs].angle,
- psegs[num_psegs].linedef,
- psegs[num_psegs].flip,
- psegs[num_psegs].dist);
- */
- num_psegs++;
- }
-
- ssectors[num_ssectors].num = num_psegs-n;
-
- num_ssectors++;
-
- return num_ssectors-1;
- }
-
- /*- translate (dx, dy) into an integer angle value (0-65535) ---------------*/
-
- unsigned ComputeAngle( int dx, int dy)
- {
- double w;
-
- w = (atan2( (double) dy , (double) dx) * (double)(65536/(M_PI*2)));
-
- if(w<0) w = (double)65536+w;
-
- return (unsigned) w;
- }
-
- /*---------------------------------------------------------------------------*
-
- This message has been taken, complete, from OBJECTS.C in DEU5beta source.
- It outlines the method used here to pick the nodelines.
-
- IF YOU ARE WRITING A DOOM EDITOR, PLEASE READ THIS:
-
- I spent a lot of time writing the Nodes builder. There are some bugs in
- it, but most of the code is OK. If you steal any ideas from this program,
- put a prominent message in your own editor to make it CLEAR that some
- original ideas were taken from DEU. Thanks.
-
- While everyone was talking about LineDefs, I had the idea of taking only
- the Segs into account, and creating the Segs directly from the SideDefs.
- Also, dividing the list of Segs in two after each call to CreateNodes makes
- the algorithm faster. I use several other tricks, such as looking at the
- two ends of a Seg to see on which side of the nodeline it lies or if it
- should be split in two. I took me a lot of time and efforts to do this.
-
- I give this algorithm to whoever wants to use it, but with this condition:
- if your program uses some of the ideas from DEU or the whole algorithm, you
- MUST tell it to the user. And if you post a message with all or parts of
- this algorithm in it, please post this notice also. I don't want to speak
- legalese; I hope that you understand me... I kindly give the sources of my
- program to you: please be kind with me...
-
- If you need more information about this, here is my E-mail address:
- quinet@montefiore.ulg.ac.be (Raphaël Quinet).
-
- Short description of the algorithm:
- 1 - Create one Seg for each SideDef: pick each LineDef in turn. If it
- has a "first" SideDef, then create a normal Seg. If it has a
- "second" SideDef, then create a flipped Seg.
- 2 - Call CreateNodes with the current list of Segs. The list of Segs is
- the only argument to CreateNodes.
- 3 - Save the Nodes, Segs and SSectors to disk. Start with the leaves of
- the Nodes tree and continue up to the root (last Node).
-
- CreateNodes does the following:
- 1 - Pick a nodeline amongst the Segs (minimize the number of splits and
- keep the tree as balanced as possible).
- 2 - Move all Segs on the right of the nodeline in a list (segs1) and do
- the same for all Segs on the left of the nodeline (in segs2).
- 3 - If the first list (segs1) contains references to more than one
- Sector or if the angle between two adjacent Segs is greater than
- 180°, then call CreateNodes with this (smaller) list. Else, create
- a SubSector with all these Segs.
- 4 - Do the same for the second list (segs2).
- 5 - Return the new node (its two children are already OK).
-
- Each time CreateSSector is called, the Segs are put in a global list.
- When there is no more Seg in CreateNodes' list, then they are all in the
- global list and ready to be saved to disk.
-
- *---------------------------------------------------------------------------*/
-
-